/*
 * ConceptInfo.java
 *
 * Created on 28 March 2007, 18:43
 *
 */

package rxtokenizer;

import java.util.*;

/**
 * Base class for various types of concepts.
 * @author Ruslan Fayzrakhmanov
 */

public abstract class ConceptType extends BaseConceptType {

    ConceptType(String name, String originalExtractingExp) {
        super(name, originalExtractingExp);
    }
    ConceptType(String name, String originalExtractingExp, String description) {
        super(name, originalExtractingExp, description);
    }
    ConceptType(String name, String originalExtractingExp, String constraintExtractingExp, String description) {
        super(name, originalExtractingExp, constraintExtractingExp, description);
    }
    
    abstract Result changeName(String newName);
    //abstract protected Result changeNameInExtrExp(String newName, String oldName);
    abstract Result checkExtractingExp();
    abstract Result fullCheckExtractingExp(ConceptTypeContainer conceptContainer, boolean checkRecursiveDependence);
    abstract Result fullCheckExtractingExp(String ExtrExp, ConceptTypeContainer conceptContainer, boolean checkRecursiveDependence);
    abstract Result changeExtractingExp(String newExtrExp, ConceptTypeContainer conceptContainer);
    abstract Result parseExtractingExp(ConceptTypeContainer conceptContainer, boolean withBaseConceptTypes);
    
    final Result changeDescription(String newDescription) {
	description = newDescription;
	return Result.NO_ERROR;
    }
    
    static final boolean isNameCorrect(String name) {
	if (name.length() == 0) return false;
	char ch;
	for (int i=0; i<name.length(); i++) {
	    ch = name.charAt(i);
	    if ( !( ((i==0)&&(isConceptNameFirstSymbol(ch)))
		|| ((i>0)&&(isConceptNameFollowSymbol(ch))) ) )
		return false;
	}
	return true;
    }
    
    protected static final boolean isConceptTypeBorder(char ch) {
        return (ch == '/')? true: false;
    }
    protected static final boolean isConceptNameFirstSymbol(char ch) {
        return (Character.isLetter(ch) || ch == '_')? true: false;
    }
    protected static final boolean isConceptNameFollowSymbol(char ch) {
        return (Character.isLetterOrDigit(ch) || ch == '_')? true: false;
    }
    protected static final boolean isAttributeNameSymbol(char ch) {
        return (Character.isLetter(ch))? true: false;
    }
    
    public final boolean isUsed() {
	if ( (derivedConceptTypes != null)&&(derivedConceptTypes.size()>0) ) return true;
	else return false;
    }

    Result free() {
	if (isUsed()) return Result.ERROR_03;
	removeFeedbacks();
	return Result.NO_ERROR;
    }
    protected final void removeFeedbacks() {
	if (baseConceptTypes == null) return;
	Iterator baseConceptIter = baseConceptTypes.iterator();
	ConceptType conceptType = null;
	while(baseConceptIter.hasNext()) {
	    conceptType = (ConceptType)baseConceptIter.next();
	    if (conceptType.derivedConceptTypes!=null)
		conceptType.getDerivedConceptTypes().remove(this);
	}
    }
    
    protected final void setFeedbacks() {
	if (baseConceptTypes == null) return;
	Iterator baseConceptIter = baseConceptTypes.iterator();
	ConceptType conceptType = null;
	while(baseConceptIter.hasNext()) {
	    conceptType = (ConceptType)baseConceptIter.next();
	    if (conceptType.derivedConceptTypes==null)
		conceptType.derivedConceptTypes = new LinkedList();
	    conceptType.getDerivedConceptTypes().add(this);
	}
    }
    
    public final Result changeConstraintExp(String newConstrainExp) {
	if (newConstrainExp.length() == 0) return Result.ERROR_26;
	clearIsExpsParsedRecursively(this);
	constraintExtractingExp = newConstrainExp;
	return Result.NO_ERROR;
    }
    // For changeConstraintExp();
    private final void clearIsExpsParsedRecursively (ConceptType conceptType) {
	if (conceptType == this) conceptType.isConstraintExpParsed = false;
	else conceptType.isExtrExpParsed = false;
	if ( (conceptType.constraints == null)||(conceptType.constraints.size() == 0) ) {
	    if ( (conceptType.derivedConceptTypes != null)&&(conceptType.derivedConceptTypes.size() > 0) ) {   
		Iterator conceptTypeIter = conceptType.derivedConceptTypes.iterator();
		ConceptType ctTmp;
		while (conceptTypeIter.hasNext()) {
		    ctTmp = (ConceptType)conceptTypeIter.next();
		    if (ctTmp.isExtrExpParsed)
			clearIsExpsParsedRecursively(ctTmp);
		}
	    }
	}
    }

    public final static Result checkConstraintExp(String extrExp) {
	if (extrExp.length() == 0) return Result.ERROR_26;
	byte state = 0;
	char ch;
	for (int i=0; i<extrExp.length(); i++) {
	    ch=extrExp.charAt(i);
	    switch (state) {
		// Outside concept declaration
		case 0: 
		    if (isConceptTypeBorder(ch))
			state = 1;
		    else
			return Result.ERROR_21;
		    break;
		// Inside concept declaration
		case 1:
		    // First symbol of the concept name
		    if (isConceptNameFirstSymbol(ch))
			state = 2;
		    else
			return Result.ERROR_13;
		    break;
		case 2:
		    // Anther symbol in concept name
		    if (isConceptNameFollowSymbol(ch))
			;
		    else if (isConceptTypeBorder(ch))
			state = 8;
		    // Concept name was readed
		    else if (ch == ' ')
			state = 3;
		    else
			return Result.ERROR_16;
		    break;
		case 3:
		    // First symbol of the attribute name
		    if (isAttributeNameSymbol(ch))
			state = 4;
		    else
			return Result.ERROR_14;
		    break;
		case 4:
		    // Anther symbol of the attribute name
		    if (isAttributeNameSymbol(ch))
			;
		    // Symbol '='
		    else if (ch == '=')
			state=5;
		    else
			return Result.ERROR_14;
		    break;
		case 5:
		    // Value of the attribute is begun to read
		    if (ch == '"')
			state = 6;
		    else
			return Result.ERROR_15;
		    break;
		case 6:
		    // Value of the attribute was readed
		    if (ch == '"')
			state = 7;
		    // Values of the attribute
		    else 
			;
		    break;
		case 7:
		    // Symbol ' '
		    if (ch == ' ')
			state = 3;
		    else if (isConceptTypeBorder(ch))
			state = 8;
		    else return Result.ERROR_15;
		    break;
		case 8:
		    return Result.ERROR_22;
	    }
	}
	if (state==8)
	    return Result.NO_ERROR;
	else
	    return Result.ERROR_16;
    }

    protected final Result changeNameInConstraintExp(String newName) {
	String extrExp = this.constraintExtractingExp;
	StringBuffer name = new StringBuffer();
	StringBuffer constraintExtractingExp = new StringBuffer();
	
	byte state = 0;
	char ch;
	for (int i=0; i<extrExp.length(); i++) {
	    ch=extrExp.charAt(i);
	    switch (state) {
		// Outside concept declaration
		case 0: 
		    if (isConceptTypeBorder(ch)) {
			constraintExtractingExp.append(ch);
			state = 1;
		    }
		    else
			return Result.ERROR_21;
		    break;
		// Inside concept declaration
		case 1:
		    // First symbol of the concept name
		    if (isConceptNameFirstSymbol(ch)) {
			name.append(ch);
			state = 2;
		    }
		    else
			return Result.ERROR_13;
		    break;
		case 2:
		    // Anther symbol in concept name
		    if (isConceptNameFollowSymbol(ch)) {
			name.append(ch);
		    }
		    else if (isConceptTypeBorder(ch)) {
			constraintExtractingExp.append(newName);
			constraintExtractingExp.append(ch);
			state = 8;
		    }
		    // Concept name was readed
		    else if (ch == ' ') {
			constraintExtractingExp.append(newName);
			constraintExtractingExp.append(ch);
			state = 3;
		    }
		    else
			return Result.ERROR_16;
		    break;
		case 3:
		    // First symbol of the attribute name
		    if (isAttributeNameSymbol(ch)) {
			constraintExtractingExp.append(ch);
			state = 4;
		    }
		    else
			return Result.ERROR_14;
		    break;
		case 4:
		    // Anther symbol of the attribute name
		    if (isAttributeNameSymbol(ch))
			;
		    // Symbol '='
		    else if (ch == '=')
			state=5;
		    else
			return Result.ERROR_14;
		    constraintExtractingExp.append(ch);
		    break;
		case 5:
		    // Value of the attribute is begun to read
		    if (ch == '"') {
			constraintExtractingExp.append(ch);
			state = 6;
		    }
		    else
			return Result.ERROR_15;
		    break;
		case 6:
		    // Value of the attribute was readed
		    if (ch == '"')
			state = 7;
		    // Values of the attribute
		    else 
			;
		    constraintExtractingExp.append(ch);
		    break;
		case 7:
		    // Symbol ' '
		    if (ch == ' ')
			state = 3;
		    else if (isConceptTypeBorder(ch))
			state = 8;
		    else return Result.ERROR_15;
		    constraintExtractingExp.append(ch);
		    break;
		case 8:
		    return Result.ERROR_22;
	    }
	}
	if (state==8) {
	    this.constraintExtractingExp = constraintExtractingExp.toString();
	    return Result.NO_ERROR;
	}
	else
	    return Result.ERROR_16;
    }
    
    public final Result fullCheckConstraintExp() {
	return fullCheckConstraintExp(constraintExtractingExp);
    }
    
    public final Result fullCheckConstraintExp(String extrExp) {
	if (extrExp.length() == 0) return Result.ERROR_26;
	
	StringBuffer name = new StringBuffer();
	//ConceptType conceptTypeTmp;
	StringBuffer attrName = new StringBuffer();
	StringBuffer attrValue = new StringBuffer();
	
	byte state = 0;
	char ch;
	for (int i=0; i<extrExp.length(); i++) {
	    ch=extrExp.charAt(i);
	    switch (state) {
		// Outside concept declaration
		case 0: 
		    if (isConceptTypeBorder(ch))
			state = 1;
		    else
			return Result.ERROR_21;
		    break;
		    // Inside concept declaration
		case 1:
		    // First symbol of the concept name
		    if (isConceptNameFirstSymbol(ch)) {
			name.append(ch);
			state = 2;
		    }
		    else
			return Result.ERROR_13;
		    break;
		case 2:
		    // Anther symbol in concept name
		    if (isConceptNameFollowSymbol(ch))
			name.append(ch);
		    // Concept name was readed
		    else if ( (ch == ' ')||isConceptTypeBorder(ch) ) {
			if (!name.toString().equals(this.name)) {
			    return Result.ERROR_24;
			}
			// Attribute declaration is expected.
			if (ch == ' ')
			    state = 3;
			// End of concept declaration in extracting expression
			else if (isConceptTypeBorder(ch))
			    state = 8;
		    }
		    else
			return Result.ERROR_13;
		    break;
		case 3:
		    // First symbol of the attribute name
		    if (isAttributeNameSymbol(ch)) {
			attrName.append(ch);
			state = 4;
		    }
		    else
			return Result.ERROR_14;
		    break;
		case 4:
		    // Anther symbol of the attribute name
		    if (isAttributeNameSymbol(ch))
			attrName.append(ch);
		    // Symbol '='
		    else if (ch == '=')
			state=5;
		    else
			return Result.ERROR_14;
		    break;
		case 5:
		    // Value of the attribute is begun to read
		    if (ch == '"')
			state = 6;
		    else
			return Result.ERROR_15;
		    break;
		case 6:
		    // Value of the attribute was readed
		    if (ch == '"') {
			Result res = ExtractiveConcept.checkAttribute(attrName.toString(), attrValue.toString());
			if (res == Result.NO_ERROR) {
			    attrName.setLength(0); attrValue.setLength(0);
			    state = 7;
			}
			else return res;
		    }
		    // Values of the attribute
		    else 
			attrValue.append(ch);
		    break;
		case 7:
		    // Symbol ' '
		    if (ch == ' ')
			state = 3;
		    else if (isConceptTypeBorder(ch))
			state = 8;
		    else return Result.ERROR_15;
		    break;
		case 8:
		    return Result.ERROR_22;
	    }
	}
	if (state==8)
	    return Result.NO_ERROR;
	else
	    return Result.ERROR_16;
    }

    final Result parseConstraintsExp(ConceptTypeContainer conceptContainer) {
	String extrExp = constraintExtractingExp;
	if (extrExp.length() == 0) return Result.ERROR_26;
	ExtractiveConcept constraint = null;
	
	StringBuffer name = new StringBuffer();
	//ConceptType conceptTypeTmp;
	StringBuffer attrName = new StringBuffer();
	StringBuffer attrValue = new StringBuffer();
	
	byte state = 0;
	char ch;
	for (int i=0; i<extrExp.length(); i++) {
	    ch=extrExp.charAt(i);
	    switch (state) {
		// Outside concept declaration
		case 0: 
		    if (isConceptTypeBorder(ch))
			state = 1;
		    else
			return Result.ERROR_21;
		    break;
		    // Inside concept declaration
		case 1:
		    // First symbol of the concept name
		    if (isConceptNameFirstSymbol(ch)) {
			name.append(ch);
			state = 2;
		    }
		    else
			return Result.ERROR_13;
		    break;
		case 2:
		    // Anther symbol in concept name
		    if (isConceptNameFollowSymbol(ch))
			name.append(ch);
		    // Concept name was readed
		    else if ( (ch == ' ')||isConceptTypeBorder(ch) ) {
			if (!name.toString().equals(this.name))
			    return Result.ERROR_24;
			constraint = new ExtractiveConcept(this);
			// Attribute declaration is expected.
			if (ch == ' ') {
			    state = 3;
			}
			// End of concept declaration in extracting expression
			else if (isConceptTypeBorder(ch))
			    state = 8;
		    }
		    else
			return Result.ERROR_13;
		    break;
		case 3:
		    // First symbol of the attribute name
		    if (isAttributeNameSymbol(ch)) {
			attrName.append(ch);
			state = 4;
		    }
		    else
			return Result.ERROR_14;
		    break;
		case 4:
		    // Another symbol of the attribute name
		    if (isAttributeNameSymbol(ch))
			attrName.append(ch);
		    // Symbol '='
		    else if (ch == '=')
			state=5;
		    else
			return Result.ERROR_14;
		    break;
		case 5:
		    // Value of the attribute is begun to read
		    if (ch == '"')
			state = 6;
		    else
			return Result.ERROR_15;
		    break;
		case 6:
		    // Value of the attribute was readed
		    if (ch == '"') {
			Result res = constraint.setAttribute(attrName.toString(), attrValue.toString());
			if (res == Result.NO_ERROR) {
			    attrName.setLength(0); attrValue.setLength(0);
			    state = 7;
			}
			else return res;
		    }
		    // Values of the attribute
		    else 
			attrValue.append(ch);
		    break;
		case 7:
		    // Symbol ' '
		    if (ch == ' ')
			state = 3;
		    else if (isConceptTypeBorder(ch)) {
			state = 8;
		    }
		    else return Result.ERROR_15;
		    break;
		case 8:
		    return Result.ERROR_22;
	    }
	}
	if (state==8) {
	    ExtractiveConcept oldConstraint = getConstraint();
	    if (oldConstraint != null) constraints.remove(oldConstraint);
	    if (constraints == null) constraints = new LinkedList();
	    constraints.addFirst(constraint);
	    isConstraintExpParsed = true;
	    return Result.NO_ERROR;
	}
	else
	    return Result.ERROR_16;
    }

    final Result deleteConstraintsExp() {
	deleteConstraintRecursively(this, null);
	return Result.NO_ERROR;
    }
    private final void deleteConstraintRecursively(ConceptType conceptType, ConceptType baseConceptType) {
	conceptType.isVisitedInRecursion = true;
	if (conceptType == this) {
	    ExtractiveConcept extractiveConcept = conceptType.getConstraint();
	    if (extractiveConcept == null)
		return;
	    else conceptType.constraints.remove(extractiveConcept);
	}
	else {
	    //rmClearConstraints(conceptType, baseConceptType);
	    ExtractiveConcept extractiveConcept = null;
	    ListIterator iter = conceptType.constraints.listIterator();
	    while (iter.hasNext()) {
		extractiveConcept = (ExtractiveConcept) iter.next();
		if (extractiveConcept.isClear() && (extractiveConcept.getTypeInfo() == baseConceptType))
		    iter.remove();
	    }
	}
	if (conceptType.constraints.size() == 0) {
		conceptType.constraints = null;
		if (conceptType.derivedConceptTypes != null) {
		    Iterator iter = conceptType.derivedConceptTypes.iterator();
		    while (iter.hasNext()) {
			ConceptType conceptTypeTmp = (ConceptType)iter.next();
			if (!conceptTypeTmp.isVisitedInRecursion)
			    deleteConstraintRecursively(conceptTypeTmp, conceptType);
		    }
		}
	}
	conceptType.isVisitedInRecursion = false;
    }

    protected final ExtractiveConcept getConstraint() {
	if (constraints == null) return null;
	ExtractiveConcept extractiveConcept = null;
	Iterator iter = constraints.iterator();
	while (iter.hasNext()) {
	    extractiveConcept = (ExtractiveConcept) iter.next();
	    if ( extractiveConcept.getTypeInfo() == this)
		return extractiveConcept;
	}
	return null;
    }

/*
    protected final LinkedList getClearConstraints(ConceptType conceptType) {
	if (constraints == null) return null;
	LinkedList Result = new LinkedList();
	ExtractiveConcept extractiveConcept = null;
	Iterator iter = constraints.iterator();
	while (iter.hasNext()) {
	    extractiveConcept = (ExtractiveConcept) iter.next();
	    if (extractiveConcept.isClear() && (extractiveConcept.getTypeInfo() == conceptType))
		Result.add(extractiveConcept);
	}
	return (Result.size() == 0)? null: Result;
    }
*/  
    /*
    private final void rmClearConstraints(ConceptType conceptType, ConceptType linkedConceptType) {
	if (conceptType.constraints == null) return;
	ExtractiveConcept extractiveConcept = null;
	ListIterator iter = conceptType.constraints.listIterator();
	while (iter.hasNext()) {
	    extractiveConcept = (ExtractiveConcept) iter.next();
	    if (extractiveConcept.isClear() && (extractiveConcept.getTypeInfo() == linkedConceptType))
		iter.remove();
	}
    }
    */

    public final boolean isExtractingExpParsed() {
	return isExtrExpParsed;
    }
    
    public final boolean isConstraintExpParsed() {
	return isConstraintExpParsed;
    }
    public final LinkedList getDerivedConceptTypes() {
	return derivedConceptTypes;
    }
    public final LinkedList getBaseConceptTypes() {
	return baseConceptTypes;
    }
    public final LinkedList getConstraints() {
	return constraints;
    }
  
    public String toString() {
	StringBuffer str = new StringBuffer();
	str.append(super.toString());
	if (isExtrExpParsed)
	    str.append("Extracting expression is parsed\n");
	else
	    str.append("Extracting expression isn't parsed\n");
	if (isVisitedInRecursion)
	    str.append("This concept type is been visiting in recursion\n");
	else
	    str.append("This concept type isn't been visiting in recursion\n");
	str.append("baseConceptTypes (begin of the list) =\n");
	if (baseConceptTypes != null) {
	    Iterator baseConceptIter = baseConceptTypes.iterator();
	    while(baseConceptIter.hasNext())
		str.append(((ConceptType)baseConceptIter.next()).name+'\n');
	}
	str.append("(end of the list)\nderivedConceptTypes (begin of the list) =\n");
	if (derivedConceptTypes != null) {
	    Iterator derivedConceptIter = derivedConceptTypes.iterator();
	    while(derivedConceptIter.hasNext())
		str.append(((ConceptType)derivedConceptIter.next()).name+'\n');
	}
	str.append("(end of the list)\nconstraints (begin of the list) =\n");
	if (constraints != null) {
	    Iterator constraintsIter = constraints.iterator();
	    while(constraintsIter.hasNext())
		str.append(constraintsIter.next().toString());//str.append(((ExtractiveConcept)constraintsIter.next()).getTypeInfo().name+'\n');
	}
	str.append("(end of the list)\n");
	return str.toString();
    }

	/**
	 * List of concept types derived from this one (List of ConceptType class instances).
	 */
    protected LinkedList derivedConceptTypes = null; // list of concepts based on this concept
	/**
	 * List of concept types on which bases this one (List of ConceptType class instances).
	 */
    protected LinkedList baseConceptTypes = null; // list of concept on which based on this concept
    /**
	 * Container constraints for extracting (List of ExtractiveConcept class instances).
	 */
    protected LinkedList constraints = null;
    protected boolean isExtrExpParsed = false;
    protected boolean isConstraintExpParsed = false;
	/**
	 * Used fror tree view.
	 */
    protected boolean isVisitedInRecursion = false;
    
}
